package com.onpositive.data.serializer.readers;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.BitSet;
import java.util.LinkedHashSet;

import com.onpositive.data.AbstractReader;
import com.onpositive.data.FilterTester;
import com.onpositive.data.ICanHaveProperty;
import com.onpositive.data.IWritable;
import com.onpositive.data.serializer.writers.AbstractWriter;
import com.onpositive.data.serializer.writers.RangedDateWriter;
import com.onpositive.data.units.DateWithAccuracy;
import com.onpositive.datamap.IDateRange;
import com.onpositive.datamap.core.IDataProperty;
import com.onpositive.datamap.values.impl.DateRange;

public class DateRangeReader extends AbstractReader<DateRange> implements
		ICanHaveProperty, IWritable {

	public DateRangeReader() {
		super(DateRange.class);
	}

	protected DateWithAccuracyReader lows = new DateWithAccuracyReader();
	protected DateWithAccuracyReader highs = new DateWithAccuracyReader();
	protected StringReader comments = new StringReader();
	private IDataProperty property;

	@Override
	public void initEmpty(int dataSize) {
		
		lows.initEmpty(dataSize);
		highs.initEmpty(dataSize);
		comments.initEmpty(dataSize);
		setInitialized();
	}
	
	@Override
	public DateRange getObject(int i) {
		if (!isInitialized()) {
			init();
		}
		if (!highs.hasValue(i))
			return null;

		DateWithAccuracy uVal = highs.getObject(i);
		if (lows.hasValue(i)) {
			DateWithAccuracy lVal = lows.getObject(i);
			return new DateRange(property, comments.getObject(i), lVal, uVal);
		}

		return new DateRange(property, comments.getObject(i), uVal);
	}

	@Override
	protected void doInit() {
		try {
			int intSize = collectionStart + myStart + INT_SIZE;
			this.file.seek(intSize);
			byte[] byteArr = new byte[12];
			file.read(byteArr, 0, 12);

			ByteBuffer bBuf = ByteBuffer.wrap(byteArr);
			IntBuffer iBuf = bBuf.asIntBuffer();
			int i1 = iBuf.get(0) + intSize + 12;
			int i2 = iBuf.get(1) + intSize + 12;
			int i3 = iBuf.get(2) + intSize + 12;
			lows.setFile(file);
			highs.setFile(file);
			comments.setFile(file);
			lows.setMyStart(intSize + 12);
			lows.setMyEnd(i1);
			highs.setMyStart(i1);
			highs.setMyEnd(i2);
			comments.setMyStart(i2);
			comments.setMyEnd(i3);

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void filterOut(LinkedHashSet<Object> obj, String text) {

	}

	@Override
	public boolean accept(int op, int a, Object constraint) {
		if (!isInitialized()) {
			init();
		}
		if (!highs.hasValue(a))
			return (op == EQ || op == CONTAINS || op == ONE_OF)
					&& constraint == null;
		if (constraint instanceof DateRange) {
			DateRange r0 = (DateRange) constraint;

			

			DateWithAccuracy hVal = highs.getObject(a);
			DateWithAccuracy lval = hVal;
			long h1 = r0.getHigh().getDate().getTime();
			long l1 = r0.getLow().getDate().getTime();
			if (lows.hasValue(a)) {
				lval = lows.getObject(a);

			}
			long l01 = lval.getDate().getTime();
			long h01 = hVal.getDate().getTime();
			switch (op) {
			// TODO THINK ABOUT WHERE CORRECTNESS SHOULD BE
			case FilterTester.GT:
				return l1 < l01;
			case FilterTester.LT:
				return h1 > l01;
			case FilterTester.GE:
				return l1 <= h01;
			case FilterTester.LE:
				return h1 >= l01;
			case FilterTester.EQ:
				return h1 == h01 && l1 == l01;
			case FilterTester.NEQ:
				return h1 != h01 || l1 != l01;
			case FilterTester.CONTAINS:
			{
				if (hVal.isNever()){
					return r0.isNever();
				}
				return (h1 >= h01&& l1<=h01)||(h1 >= l01&& l1<=l01);
			}
			default:
				break;
			}
		}
		if (highs.accept(op, a, constraint)
				|| (lows.hasValue(a) && lows.accept(op, a, constraint))) {
			return true;
		}
		return false;
	}

	@Override
	public void seProperty(IDataProperty p) {
		this.property = p;
	}

	@Override
	public void setValue(int index, Object value) {
		if (!isInitialized()){
			init();
		}
		if (value instanceof DateWithAccuracy){
			value=new DateRange(property, "", (DateWithAccuracy) value);
		}
		IDateRange r = (IDateRange) value;
		if (r != null) {
			lows.setValue(index, r.getLow());
			highs.setValue(index, r.getHigh());
			comments.setValue(index, r.getComment());
		}
		else{
			lows.setValue(index, null);
			highs.setValue(index, null);
			comments.setValue(index, null);
		}
	}

	@Override
	public void serialize(DataOutputStream ds) throws IOException {
		RangedDateWriter wm = new RangedDateWriter();
		for (int a = 0; a < lows.size; a++) {
			wm.append(getObject(a));
		}
		wm.serialize(ds);
	}

	@Override
	public Object aggregate(int op, BitSet ss) {
		if (op==AGR_MIN||op==AGR_MAX){
			if (op==AGR_MIN){
				Object aggregate = lows.aggregate(op, ss);
				return aggregate;
			}
			if (op==AGR_MAX){
				Object aggregate = highs.aggregate(op, ss);
				return aggregate;
			}
		}
		return null;
	}

	@Override
	public AbstractWriter<DateRange> getWriter() {
		return new RangedDateWriter();
	}

	@Override
	public void expand(int newSize) {
		if (!isInitialized()){
			init();
		}
		lows.expand(newSize);
		highs.expand(newSize);
		comments.expand(newSize);		
	}
	

}
